﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MvCamCtrl.NET;
using System.Runtime.InteropServices;
using VisionApp.Entity;
using VisionApp.Interface;
using System.Threading;
using System.Timers;
using GalaSoft.MvvmLight.Messaging;
using OpenCvSharp;
using System.Configuration;
using System.Windows.Forms;
using System.IO;

namespace VisionApp.Camera
{
    public class HIKCamera1 : ICamera
    {
        public bool g_bExit = false;
        public bool g_bExit1 = false;
        public PictureBox PictureBoxVideoLeft { get; set; }
        public PictureBox PictureBoxVideoRight { get; set; }
        public PictureBox PictureBoxImageLeft { get; set; }
        public PictureBox PictureBoxImageRight { get; set; }
        public HImageContainer ImCtnr { get; set; }
        public bool Connected { get; set; }
        public List<string> DeviceIDList { get; set; }
        public CameraInfo CurrentCameraInfo { get; set; }
        private Logger _log;
        private int nRet = MyCamera.MV_OK;
        private Dictionary<string, MyCamera> _deviceDict = new Dictionary<string, MyCamera>();
        private Dictionary<string, Thread> _threadDict = new Dictionary<string, Thread>();
        private Thread hReceiveImageThreadHandle;
        private Thread hShowImageThreadHandle;
        private object _bufferLock = new object();
        private byte[] _imgBuffer;
        private int _imgWidth;
        private int _imgHeight;
        private ImagePosID _id;
        private uint bAutoExposure = Convert.ToUInt32(ConfigurationManager.AppSettings["AutoExposure"]);
        private uint Exposure = Convert.ToUInt32(ConfigurationManager.AppSettings["Exposure"]);
        private uint bAutoGain = Convert.ToUInt32(ConfigurationManager.AppSettings["AutoGain"]);
        private uint maxExposure = Convert.ToUInt32(ConfigurationManager.AppSettings["MaxExposure"]);
        private uint maxGain = Convert.ToUInt32(ConfigurationManager.AppSettings["MaxGain"]);
        private uint maxThroughput = Convert.ToUInt32(ConfigurationManager.AppSettings["DeviceMaxThroughput"]);

        public HIKCamera1(ILogger log)
        {
            _log = (Logger)log;
            ImCtnr = new HImageContainer();
        }
        public int Init()
        {
            MyCamera.MV_GIGE_DEVICE_INFO stGigEDeviceInfo=new MyCamera.MV_GIGE_DEVICE_INFO();
            try
            {
                // ch:枚举设备 | en:Enum device
                MyCamera.MV_CC_DEVICE_INFO_LIST stDevList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
                nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref stDevList);
                if (MyCamera.MV_OK != nRet)
                {
                    _log.Error(string.Format("Enum device failed:{0:x8}", nRet));
                    return -1;
                }
                _log.Imformation("Enum device count : " + Convert.ToString(stDevList.nDeviceNum));
                if (0 == stDevList.nDeviceNum)
                {
                    return -1;
                }
                MyCamera.MV_CC_DEVICE_INFO stDevInfo;  
                // ch:打印设备信息 en:Print device info
                for (Int32 i = 0; i < stDevList.nDeviceNum; i++)
                {
                    stDevInfo = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(stDevList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                    if (MyCamera.MV_GIGE_DEVICE == stDevInfo.nTLayerType)
                    {
                        stGigEDeviceInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(stDevInfo.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));
                        uint nIp1 = ((stGigEDeviceInfo.nCurrentIp & 0xff000000) >> 24);
                        uint nIp2 = ((stGigEDeviceInfo.nCurrentIp & 0x00ff0000) >> 16);
                        uint nIp3 = ((stGigEDeviceInfo.nCurrentIp & 0x0000ff00) >> 8);
                        uint nIp4 = (stGigEDeviceInfo.nCurrentIp & 0x000000ff);
                        _log.Imformation("\n" + i.ToString() + ": [GigE] User Define Name : " + stGigEDeviceInfo.chUserDefinedName);
                        _log.Imformation("device IP :" + nIp1 + "." + nIp2 + "." + nIp3 + "." + nIp4);
                    }
                    else if (MyCamera.MV_USB_DEVICE == stDevInfo.nTLayerType)
                    {
                        MyCamera.MV_USB3_DEVICE_INFO stUsb3DeviceInfo = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(stDevInfo.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                        _log.Imformation("\n" + i.ToString() + ": [U3V] User Define Name : " + stUsb3DeviceInfo.chUserDefinedName);
                        _log.Imformation("\n Serial Number : " + stUsb3DeviceInfo.chSerialNumber);
                        _log.Imformation("\n Device Number : " + stUsb3DeviceInfo.nDeviceNumber);
                    }
                    stDevInfo = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(stDevList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                    // 打开特定的相机
                    if(!DeviceIDList.Contains(stGigEDeviceInfo.chUserDefinedName))
                    {
                        continue;
                    }
                    MyCamera device = new MyCamera();
                    // ch:创建设备 | en:Create device
                    nRet = device.MV_CC_CreateDevice_NET(ref stDevInfo);
                    if (MyCamera.MV_OK != nRet)
                    {
                        _log.Imformation(string.Format("Create device failed:{0:x8}", nRet));
                        return -1;
                    }
                    // ch:打开设备 | en:Open device
                    nRet = device.MV_CC_OpenDevice_NET();
                    if (MyCamera.MV_OK != nRet)
                    {
                        _log.Imformation(string.Format("Open device failed:{0:x8}", nRet));
                        return -1;
                    }
                    // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
                    if (stDevInfo.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                    {
                        int nPacketSize = device.MV_CC_GetOptimalPacketSize_NET();
                        if (nPacketSize > 0)
                        {
                            nRet = device.MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
                            if (nRet != MyCamera.MV_OK)
                            {
                                _log.Imformation(string.Format("Warning: Set Packet Size failed {0:x8}", nRet));
                            }
                        }
                        else
                        {
                            _log.Imformation(string.Format("Warning: Get Packet Size failed {0:x8}", nPacketSize));
                        }
                    }
                    // ch:设置触发模式为off || en:set trigger mode as off
                    if (MyCamera.MV_OK != device.MV_CC_SetEnumValue_NET("TriggerMode", 0))
                    {
                        _log.Imformation("Set TriggerMode failed!");
                        return -1;
                    }
                    if (MyCamera.MV_OK != device.MV_CC_SetEnumValue_NET("ExposureAuto", bAutoExposure))
                    {
                        _log.Imformation("Set ExposureAuto failed!");
                    }
                    if (MyCamera.MV_OK != device.MV_CC_SetEnumValue_NET("GainAuto", bAutoGain))
                    {
                        _log.Imformation("Set GainAuto failed!");
                    }
                    if (MyCamera.MV_OK != device.MV_CC_SetIntValue_NET("AutoExposureTimeUpperLimit", maxExposure))
                    {
                        _log.Imformation("Set AutoExposureTimeUpperLimit failed!");
                    }
                    if (MyCamera.MV_OK != device.MV_CC_SetIntValueEx_NET("DeviceMaxThroughput", maxThroughput))
                    {
                        _log.Imformation("Set DeviceMaxThroughput failed!");
                    }
                    _log.Imformation("User Device ID: "+ stGigEDeviceInfo.chUserDefinedName);
                    _deviceDict.Add(stGigEDeviceInfo.chUserDefinedName, device);
                }
                //hShowImageThreadHandle = new Thread(showImage);
                //hShowImageThreadHandle.Start();
                Connected = true;
                return 0;
            }
            catch (Exception e)
            {
                _log.Error("Init Error ",e);
                return -1;
            }
        }
        public int GrabImg()
        {
            try
            {
                MyCamera device = _deviceDict[CurrentCameraInfo.DeviceID];
                do
                {
                    if(CurrentCameraInfo.RecordMode=="Video")
                    {
                        MyCamera.MV_CC_RECORD_PARAM stRecordPar = new MyCamera.MV_CC_RECORD_PARAM();
                        MyCamera.MVCC_INTVALUE stParam = new MyCamera.MVCC_INTVALUE();
                        nRet = device.MV_CC_GetIntValue_NET("Width", ref stParam);
                        if (MyCamera.MV_OK != nRet)
                        {
                            _log.Error("Get Width failed: nRet {0:x8}" + nRet);
                            break;
                        }
                        stRecordPar.nWidth = (ushort)stParam.nCurValue;

                        nRet = device.MV_CC_GetIntValue_NET("Height", ref stParam);
                        if (MyCamera.MV_OK != nRet)
                        {
                            _log.Error("Get Height failed: nRet {0:x8}" + nRet);
                            break;
                        }
                        stRecordPar.nHeight = (ushort)stParam.nCurValue;

                        MyCamera.MVCC_ENUMVALUE stEnumValue = new MyCamera.MVCC_ENUMVALUE();
                        nRet = device.MV_CC_GetEnumValue_NET("PixelFormat", ref stEnumValue);
                        if (MyCamera.MV_OK != nRet)
                        {
                            _log.Error("Get Width failed: nRet {0:x8}" + nRet);
                            break;
                        }
                        stRecordPar.enPixelType = (MyCamera.MvGvspPixelType)stEnumValue.nCurValue;

                        MyCamera.MVCC_FLOATVALUE stFloatValue = new MyCamera.MVCC_FLOATVALUE();
                        nRet = device.MV_CC_GetFloatValue_NET("ResultingFrameRate", ref stFloatValue);
                        if (MyCamera.MV_OK != nRet)
                        {
                            _log.Error("Get Float value failed: nRet {0:x8}" + nRet);
                            break;
                        }
                        _log.Imformation(CurrentCameraInfo.DeviceID + "ResultingFrameRate:" + stFloatValue.fCurValue);
                        // ch:帧率(大于1/16)fps | en:Frame Rate (>1/16)fps
                        stRecordPar.fFrameRate = stFloatValue.fCurValue;
                        // ch:码率kbps(128kbps-16Mbps) | en:Bitrate kbps(128kbps-16Mbps)
                        stRecordPar.nBitRate = 3348;
                        // ch:录像格式(仅支持AVI) | en:Record Format(AVI is only supported)
                        stRecordPar.enRecordFmtType = MyCamera.MV_RECORD_FORMAT_TYPE.MV_FormatType_AVI;
                        stRecordPar.strFilePath = CurrentCameraInfo.RecordPath + CurrentCameraInfo.PosID.ToString() + "Recording.avi";
                        nRet = device.MV_CC_StartRecord_NET(ref stRecordPar);
                        if (MyCamera.MV_OK != nRet)
                        {
                            _log.Error("Start Record failed: nRet {0:x8}" + nRet);
                            break;
                        }
                    }
                    // ch:开启抓图 | en:start grab
                    nRet = device.MV_CC_StartGrabbing_NET();
                    if (MyCamera.MV_OK != nRet)
                    {
                        _log.Error("Start grabbing failed:{0:x8}"+ nRet);                                            
                        break;
                    }
                    CameraHandle ch = new CameraHandle();
                    ch.Device = device;
                    ch.ID = CurrentCameraInfo.PosID;
                    hReceiveImageThreadHandle = new Thread(ReceiveImageWorkThread);
                    hReceiveImageThreadHandle.Start(ch);
                    _threadDict.Add(CurrentCameraInfo.DeviceID, hReceiveImageThreadHandle);
                } while (false);
                if (MyCamera.MV_OK != nRet)
                {
                    // ch:销毁设备 | en:Destroy device
                    nRet = device.MV_CC_DestroyDevice_NET();
                    if (MyCamera.MV_OK != nRet)
                    {
                        _log.Error("Destroy device failed:{0:x8}"+ nRet);
                    }
                }
                return 0;
            }
            catch (Exception e)
            {
                _log.Error("GrabImg Error " + e.ToString(),e);
                return -1;
            }
        }
        public int StopRecord()
        {
            try
            {
                MyCamera device = _deviceDict[CurrentCameraInfo.DeviceID];
                Thread t = _threadDict[CurrentCameraInfo.DeviceID];
                g_bExit = true;
                t.Join();
                _threadDict.Remove(CurrentCameraInfo.DeviceID);
                do
                {
                    // ch:停止抓图 | en:Stop grab image
                    nRet = device.MV_CC_StopGrabbing_NET();
                    if (MyCamera.MV_OK != nRet)
                    {
                        _log.Error("Stop grabbing failed:{0:x8}" + nRet);
                        break;
                    }
                    if(CurrentCameraInfo.RecordMode=="Video")
                    {
                        // ch:停止录像 | en:Stop record
                        nRet = device.MV_CC_StopRecord_NET();
                        if (MyCamera.MV_OK != nRet)
                        {
                            _log.Error("Stop Record failed:{0:x8}" + nRet);
                            break;
                        }
                    }
                } while (false);
                if (MyCamera.MV_OK != nRet)
                {
                    // ch:销毁设备 | en:Destroy device
                    nRet = device.MV_CC_DestroyDevice_NET();
                    if (MyCamera.MV_OK != nRet)
                    {
                        _log.Error("Destroy device failed:{0:x8}" + nRet);
                    }
                }
                return 0;
            }
            catch(Exception e)
            {
                _log.Error("StopRecord:" + e.ToString());
                return -1;
            }
        }
        public int Exit()
        {
            try
            {
                g_bExit = false;
                //hShowImageThreadHandle?.Abort();
                foreach (var device in _deviceDict)
                {
                    // ch:关闭设备 | en:Close device
                    nRet = device.Value.MV_CC_CloseDevice_NET();
                    if (MyCamera.MV_OK != nRet)
                    {
                        _log.Error("Close device failed{0:x8}"+nRet);
                        break;
                    }

                    // ch:销毁设备 | en:Destroy device
                    nRet = device.Value.MV_CC_DestroyDevice_NET();
                    if (MyCamera.MV_OK != nRet)
                    {
                        _log.Error("Destroy device failed:{0:x8}"+ nRet);
                        break;
                    }
                }
                _deviceDict.Clear();
                Connected = false;
                return 0;
            }
            catch (Exception e)
            {
                _log.Error("Exit Error ", e);
                return -1;
            }
        }
        public void ReceiveImageWorkThread(object obj)
        {
            try
            {
                int nRet = MyCamera.MV_OK;
                CameraHandle ch = obj as CameraHandle;
                MyCamera device = ch.Device;
                MyCamera.MV_FRAME_OUT stImageOut = new MyCamera.MV_FRAME_OUT();
                MyCamera.MV_CC_INPUT_FRAME_INFO stInputFrameInfo = new MyCamera.MV_CC_INPUT_FRAME_INFO();
                MyCamera.MV_DISPLAY_FRAME_INFO stDisplayInfo = new MyCamera.MV_DISPLAY_FRAME_INFO();
                g_bExit = false;
                while (true)
                {
                    nRet = device.MV_CC_GetImageBuffer_NET(ref stImageOut, 1000);
                    if (nRet == MyCamera.MV_OK)
                    {
                        _log.Imformation("Get Image Buffer:" + "Width[" + Convert.ToString(stImageOut.stFrameInfo.nWidth) + "] , Height[" + Convert.ToString(stImageOut.stFrameInfo.nHeight)
                            + "] , FrameNum[" + Convert.ToString(stImageOut.stFrameInfo.nFrameNum) + "]");
                        if (CurrentCameraInfo.RecordMode == "Video")
                        {
                            stInputFrameInfo.pData = stImageOut.pBufAddr;
                            stInputFrameInfo.nDataLen = stImageOut.stFrameInfo.nFrameLen;
                            nRet = device.MV_CC_InputOneFrame_NET(ref stInputFrameInfo);
                            if (MyCamera.MV_OK != nRet)
                            {
                                _log.Error("Input one frame failed: nRet {0:x8}" + nRet);
                            }
                            if(ch.ID==ImagePosID.LEFTCAM)
                            {
                                stDisplayInfo.hWnd = PictureBoxVideoLeft.Handle;
                            }
                            else
                            {
                                stDisplayInfo.hWnd = PictureBoxVideoRight.Handle;
                            }
                            stDisplayInfo.pData = stImageOut.pBufAddr;
                            stDisplayInfo.nDataLen = stImageOut.stFrameInfo.nFrameLen;
                            stDisplayInfo.nWidth = stImageOut.stFrameInfo.nWidth;
                            stDisplayInfo.nHeight = stImageOut.stFrameInfo.nHeight;
                            stDisplayInfo.enPixelType = stImageOut.stFrameInfo.enPixelType;
                            device.MV_CC_DisplayOneFrame_NET(ref stDisplayInfo);
                        }
                        else
                        {
                            MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM();
                            if (stImageOut.stFrameInfo.nFrameLen == 0)
                            {
                                _log.Error("Save Bmp Fail!");
                                return;
                            }
                            stSaveFileParam.enImageType = MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Png;
                            stSaveFileParam.enPixelType = stImageOut.stFrameInfo.enPixelType;
                            stSaveFileParam.pData = stImageOut.pBufAddr;
                            stSaveFileParam.nDataLen = stImageOut.stFrameInfo.nFrameLen;
                            stSaveFileParam.nHeight = stImageOut.stFrameInfo.nHeight;
                            stSaveFileParam.nWidth = stImageOut.stFrameInfo.nWidth;
                            stSaveFileParam.iMethodValue = 2;
                            stSaveFileParam.pImagePath = CurrentCameraInfo.ImagePath + ch.ID.ToString() + DateTime.Now.ToString("mmssfff") + ".png";
                            nRet = device.MV_CC_SaveImageToFile_NET(ref stSaveFileParam);
                            if (MyCamera.MV_OK != nRet)
                            {
                                _log.Error("Save Png Fail!" + nRet);
                                return;
                            }
                            if (ch.ID == ImagePosID.LEFTCAM)
                            {
                                stDisplayInfo.hWnd = PictureBoxImageLeft.Handle;
                            }
                            else
                            {
                                stDisplayInfo.hWnd = PictureBoxImageRight.Handle;
                            }
                            stDisplayInfo.pData = stImageOut.pBufAddr;
                            stDisplayInfo.nDataLen = stImageOut.stFrameInfo.nFrameLen;
                            stDisplayInfo.nWidth = stImageOut.stFrameInfo.nWidth;
                            stDisplayInfo.nHeight = stImageOut.stFrameInfo.nHeight;
                            stDisplayInfo.enPixelType = stImageOut.stFrameInfo.enPixelType;
                            device.MV_CC_DisplayOneFrame_NET(ref stDisplayInfo);
                            System.Threading.Thread.Sleep(CurrentCameraInfo.TimeGap);
                        }
                        device.MV_CC_FreeImageBuffer_NET(ref stImageOut);
                    }
                    else
                    {
                        _log.Error("Get Image failed:{0:x8}" + nRet);
                    }
                    if (g_bExit)
                    {
                        break;
                    }
                }
                _log.Imformation("录屏线程" + ch.ID + "结束");
            }
            catch(Exception e)
            {
                _log.Error("ReceiveImageWorkThread", e);
            }
        }
        private void showImage()
        {
            try
            {
                IntPtr pBufSrc = IntPtr.Zero;
                int width, height;
                ImagePosID id;
                while (true)
                {
                    //if (g_bExit)
                    //{
                    //    break;
                    //}
                    System.Threading.Thread.Sleep(100);
                    if (_imgBuffer == null)
                    {
                        continue;
                    }
                    lock (_bufferLock)
                    {
                        if (pBufSrc == IntPtr.Zero)
                        {
                            pBufSrc = Marshal.AllocHGlobal((int)_imgBuffer.Length);
                        }
                        Marshal.Copy(_imgBuffer, 0, pBufSrc, (int)_imgBuffer.Length);
                        width = _imgWidth;
                        height = _imgHeight;
                        id = _id;
                    }
                    //MyCamera.MV_PIXEL_CONVERT_PARAM stConvertPixelParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();
                    //stConvertPixelParam.nWidth = (ushort)width;
                    //stConvertPixelParam.nHeight = (ushort)height;
                    //stConvertPixelParam.pSrcData = pBufSrc;
                    //stConvertPixelParam.nSrcDataLen = (uint)(width*height);
                    //stConvertPixelParam.enSrcPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8;
                    //stConvertPixelParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
                    //stConvertPixelParam.pDstBuffer = pBufForConvert;
                    //stConvertPixelParam.nDstBufferSize = (uint)(width * height * 3);

                    //nRet = device.MV_CC_ConvertPixelType_NET(ref stConvertPixelParam);
                    //if (MyCamera.MV_OK != nRet)
                    //{
                    //    _log.Error("Convert pixel type Failed:{0:x8}" + nRet);
                    //    break;
                    //}
                    byte[] data = new byte[width * height];
                    Marshal.Copy(pBufSrc, data, 0, (int)(width * height));
                    HImageContainer hic = new HImageContainer();
                    hic.TextWidth = width;
                    hic.TextHeight = height;
                    hic.ImGray = data;
                    hic.ID = id;
                    Messenger.Default.Send<HImageContainer>(hic);
                }
                if (pBufSrc != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pBufSrc);
                }
                _log.Imformation("显示线程结束");
            }
            catch(Exception e)
            {
                _log.Error("showImage:" +e.ToString());
            }
        }
    }
    public class CameraHandle
    {
        public MyCamera Device { get; set; }
        public ImagePosID ID { get; set; }
    }

}
